use num_traits::{Float, Num, NumCast};
use rand::{Rng, thread_rng};
use rand::distributions::Uniform;
use crate::Tensor;

pub fn tensor<T, D>(data: D, shape: &[usize], requires_grad: bool) -> Tensor<T>
    where
        T: Copy + Num + 'static,
        D: AsRef<[T]>,
{
    let data = data
        .as_ref()
        .to_vec()
        .into_boxed_slice();
    let shape = shape
        .to_vec()
        .into_boxed_slice();

    let mut tensor: Tensor<T> = (data.into(), shape).into();
    tensor.requires_grad_(requires_grad);
    tensor
}

pub fn scalar<T>(value: T) -> Tensor<T>
    where
        T: Copy + Num + 'static,
{
    Tensor::scalar(value)
}

pub fn rand<T>(shape: &[usize], requires_grad: bool) -> Tensor<T>
    where
        T: Copy + Num + NumCast + 'static,
{
    let data = thread_rng()
        .sample_iter(Uniform::new(-0.1, 0.1)) // TODO
        .take(shape.iter().product())
        .map(|x| T::from(x).unwrap())
        .collect::<Box<[T]>>();

    let mut tensor: Tensor<T> = (data.into(), shape.to_vec().into_boxed_slice()).into();
    tensor.requires_grad_(requires_grad);
    tensor
}
